package drds.data_migrate.vo;


import drds.common.Author;
import drds.data_migrate.Authors;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 所有的状态及统计信息交互类，job、taskGroup、task等的消息汇报都走该类
 */
@Author(name = Authors.LI_YANG)
public class State implements Cloneable
{
    /**
     * task给job的信息 *
     */
    @Setter
    @Getter
    Map<String, List<String>> taskIdToMessageListMap;
    /**
     * 所有的数值key-value对 *
     */
    @Setter
    @Getter
    private Map<String, Number> keyToNumberMap;
    /**
     * 运行状态 *
     */
    @Setter
    @Getter
    private Status status;
    /**
     * 异常记录 *
     */
    @Setter
    @Getter
    private Throwable throwable;
    /**
     * 记录的timestamp *
     */
    @Setter
    @Getter
    private long timestamp;

    public State()
    {
        this.init();
    }

    public synchronized void reset()
    {
        this.init();
    }

    private void init()
    {
        this.keyToNumberMap = new ConcurrentHashMap<String, Number>();
        this.status = Status.running;
        this.throwable = null;
        this.taskIdToMessageListMap = new ConcurrentHashMap<String, List<String>>();
        this.timestamp = System.currentTimeMillis();
    }


    public synchronized void setState(Status status)
    {
        setState(status, false);
    }

    public synchronized void setState(Status status, boolean isForce)
    {
        if (!isForce && this.status.equals(Status.failed))
        {
            return;
        }

        this.status = status;
    }


    public void setThrowable(Throwable throwable)
    {
        setThrowable(throwable, false);
    }


    public synchronized void setThrowable(Throwable throwable, boolean isForce)
    {
        if (isForce)
        {
            this.throwable = throwable;
        } else
        {
            this.throwable = this.throwable == null ? throwable : this.throwable;
        }
    }


    public List<String> getMessage(final String key)
    {
        return taskIdToMessageListMap.get(key);
    }


    public synchronized Long getLong(final String key)
    {
        Number value = this.keyToNumberMap.get(key);

        return value == null ? 0 : value.longValue();
    }

    public synchronized void setLong(final String key, final long value)
    {
        Validate.isTrue(StringUtils.isNotBlank(key), "设置counter的key不能为空");
        this.keyToNumberMap.put(key, value);
    }


    public synchronized void setDouble(final String key, final double value)
    {
        Validate.isTrue(StringUtils.isNotBlank(key), "设置counter的key不能为空");
        this.keyToNumberMap.put(key, value);
    }

    public synchronized void increase(final String key, final long deltaValue)
    {
        Validate.isTrue(StringUtils.isNotBlank(key), "增加counter的key不能为空");

        long value = this.getLong(key);

        this.keyToNumberMap.put(key, value + deltaValue);
    }

    @Override
    public State clone()
    {
        State state = new State();

        /**
         * clone keyToNumberMap
         */
        if (this.keyToNumberMap != null)
        {
            for (Entry<String, Number> entry : this.keyToNumberMap.entrySet())
            {
                String key = entry.getKey();
                Number value = entry.getValue();
                if (value instanceof Long)
                {
                    state.setLong(key, (Long) value);
                } else if (value instanceof Double)
                {
                    state.setDouble(key, (Double) value);
                }
            }
        }

        state.setState(this.status, true);
        state.setThrowable(this.throwable, true);
        state.setTimestamp(this.timestamp);

        /**
         * clone taskIdToMessageListMap
         */
        if (this.taskIdToMessageListMap != null)
        {
            for (final Entry<String, List<String>> entry : this.taskIdToMessageListMap.entrySet())
            {
                String key = entry.getKey();
                List value = new ArrayList()
                {{
                    addAll(entry.getValue());
                }};
                state.getTaskIdToMessageListMap().put(key, value);
            }
        }

        return state;
    }


    public synchronized boolean isFinished()
    {
        return this.status == Status.succeeded || this.status == Status.failed
                || this.status == Status.killed;
    }

}
